home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / ai_main.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  36.2 KB  |  1,327 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3.  
  4. /*****************************************************************************
  5.  * name:        ai_main.c
  6.  *
  7.  * desc:        Quake3 bot AI
  8.  *
  9.  * $Archive: /source/code/game/ai_main.c $
  10.  * $Author: Raduffy $ 
  11.  * $Revision: 7 $
  12.  * $Modtime: 1/14/00 5:28p $
  13.  * $Date: 1/14/00 5:35p $
  14.  *
  15.  *****************************************************************************/
  16.  
  17.  
  18. #include "g_local.h"
  19. #include "q_shared.h"
  20. #include "botlib.h"        //bot lib interface
  21. #include "be_aas.h"
  22. #include "be_ea.h"
  23. #include "be_ai_char.h"
  24. #include "be_ai_chat.h"
  25. #include "be_ai_gen.h"
  26. #include "be_ai_goal.h"
  27. #include "be_ai_move.h"
  28. #include "be_ai_weap.h"
  29. //
  30. #include "ai_main.h"
  31. #include "ai_dmq3.h"
  32. #include "ai_chat.h"
  33. #include "ai_cmd.h"
  34. #include "ai_dmnet.h"
  35. //
  36. #include "chars.h"
  37. #include "inv.h"
  38. #include "syn.h"
  39.  
  40. #define MAX_PATH        144
  41.  
  42.  
  43. //bot states
  44. bot_state_t    *botstates[MAX_CLIENTS];
  45. //number of bots
  46. int numbots;
  47. //time to do a regular update
  48. float regularupdate_time;
  49. //
  50. int bot_interbreed;
  51. int bot_interbreedmatchcount;
  52. //
  53. vmCvar_t bot_thinktime;
  54. vmCvar_t bot_memorydump;
  55. vmCvar_t bot_pause;
  56. vmCvar_t bot_report;
  57. vmCvar_t bot_testsolid;
  58. vmCvar_t bot_interbreedchar;
  59. vmCvar_t bot_interbreedbots;
  60. vmCvar_t bot_interbreedcycle;
  61. vmCvar_t bot_interbreedwrite;
  62.  
  63.  
  64. void ExitLevel( void );
  65.  
  66.  
  67. /*
  68. ==================
  69. BotAI_Print
  70. ==================
  71. */
  72. void QDECL BotAI_Print(int type, char *fmt, ...) {
  73.     char str[2048];
  74.     va_list ap;
  75.  
  76.     va_start(ap, fmt);
  77.     vsprintf(str, fmt, ap);
  78.     va_end(ap);
  79.  
  80.     switch(type) {
  81.         case PRT_MESSAGE: {
  82.             G_Printf("%s", str);
  83.             break;
  84.         }
  85.         case PRT_WARNING: {
  86.             G_Printf( S_COLOR_YELLOW "Warning: %s", str );
  87.             break;
  88.         }
  89.         case PRT_ERROR: {
  90.             G_Printf( S_COLOR_RED "Error: %s", str );
  91.             break;
  92.         }
  93.         case PRT_FATAL: {
  94.             G_Printf( S_COLOR_RED "Fatal: %s", str );
  95.             break;
  96.         }
  97.         case PRT_EXIT: {
  98.             G_Error( S_COLOR_RED "Exit: %s", str );
  99.             break;
  100.         }
  101.         default: {
  102.             G_Printf( "unknown print type\n" );
  103.             break;
  104.         }
  105.     }
  106. }
  107.  
  108.  
  109. /*
  110. ==================
  111. BotAI_Trace
  112. ==================
  113. */
  114. void BotAI_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) {
  115.     trace_t trace;
  116.  
  117.     trap_Trace(&trace, start, mins, maxs, end, passent, contentmask);
  118.     //copy the trace information
  119.     bsptrace->allsolid = trace.allsolid;
  120.     bsptrace->startsolid = trace.startsolid;
  121.     bsptrace->fraction = trace.fraction;
  122.     VectorCopy(trace.endpos, bsptrace->endpos);
  123.     bsptrace->plane.dist = trace.plane.dist;
  124.     VectorCopy(trace.plane.normal, bsptrace->plane.normal);
  125.     bsptrace->plane.signbits = trace.plane.signbits;
  126.     bsptrace->plane.type = trace.plane.type;
  127.     bsptrace->surface.value = trace.surfaceFlags;
  128.     bsptrace->ent = trace.entityNum;
  129.     bsptrace->exp_dist = 0;
  130.     bsptrace->sidenum = 0;
  131.     bsptrace->contents = 0;
  132. }
  133.  
  134. /*
  135. ==================
  136. BotAI_GetClientState
  137. ==================
  138. */
  139. int BotAI_GetClientState( int clientNum, playerState_t *state ) {
  140.     gentity_t    *ent;
  141.  
  142.     ent = &g_entities[clientNum];
  143.     if ( !ent->inuse ) {
  144.         return qfalse;
  145.     }
  146.     if ( !ent->client ) {
  147.         return qfalse;
  148.     }
  149.  
  150.     memcpy( state, &ent->client->ps, sizeof(playerState_t) );
  151.     return qtrue;
  152. }
  153.  
  154. /*
  155. ==================
  156. BotAI_GetEntityState
  157. ==================
  158. */
  159. int BotAI_GetEntityState( int entityNum, entityState_t *state ) {
  160.     gentity_t    *ent;
  161.  
  162.     ent = &g_entities[entityNum];
  163.     memset( state, 0, sizeof(entityState_t) );
  164.     if (!ent->inuse) return qfalse;
  165.     if (!ent->r.linked) return qfalse;
  166.     if (ent->r.svFlags & SVF_NOCLIENT) return qfalse;
  167.     memcpy( state, &ent->s, sizeof(entityState_t) );
  168.     return qtrue;
  169. }
  170.  
  171. /*
  172. ==================
  173. BotAI_GetSnapshotEntity
  174. ==================
  175. */
  176. int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state ) {
  177.     int        entNum;
  178.  
  179.     entNum = trap_BotGetSnapshotEntity( clientNum, sequence );
  180.     if ( entNum == -1 ) {
  181.         memset(state, 0, sizeof(entityState_t));
  182.         return -1;
  183.     }
  184.  
  185.     BotAI_GetEntityState( entNum, state );
  186.  
  187.     return sequence + 1;
  188. }
  189.  
  190. /*
  191. ==================
  192. BotAI_BotInitialChat
  193. ==================
  194. */
  195. void QDECL BotAI_BotInitialChat( bot_state_t *bs, char *type, ... ) {
  196.     int        i, mcontext;
  197.     va_list    ap;
  198.     char    *p;
  199.     char    *vars[MAX_MATCHVARIABLES];
  200.  
  201.     memset(vars, 0, sizeof(vars));
  202.     va_start(ap, type);
  203.     p = va_arg(ap, char *);
  204.     for (i = 0; i < MAX_MATCHVARIABLES; i++) {
  205.         if( !p ) {
  206.             break;
  207.         }
  208.         vars[i] = p;
  209.         p = va_arg(ap, char *);
  210.     }
  211.     va_end(ap);
  212.  
  213.     mcontext = CONTEXT_NORMAL|CONTEXT_NEARBYITEM|CONTEXT_NAMES;
  214.     if (BotCTFTeam(bs) == CTF_TEAM_RED) mcontext |= CONTEXT_CTFREDTEAM;
  215.     else mcontext |= CONTEXT_CTFBLUETEAM;
  216.  
  217.     trap_BotInitialChat( bs->cs, type, mcontext, vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7] );
  218. }
  219.  
  220.  
  221. /*
  222. ==================
  223. BotTestSolid
  224. ==================
  225. */
  226. void BotTestSolid(vec3_t origin) {
  227.     int areanum;
  228.  
  229.     trap_Cvar_Update(&bot_testsolid);
  230.     if (bot_testsolid.integer) {
  231.         if (!trap_AAS_Initialized()) return;
  232.         areanum = BotPointAreaNum(origin);
  233.         if (areanum) BotAI_Print(PRT_MESSAGE, "\remtpy area");
  234.         else BotAI_Print(PRT_MESSAGE, "\r^1SOLID area");
  235.     }
  236. }
  237.  
  238. /*
  239. ==================
  240. BotReportStatus
  241. ==================
  242. */
  243. void BotReportStatus(bot_state_t *bs) {
  244.     char goalname[MAX_MESSAGE_SIZE];
  245.     char netname[MAX_MESSAGE_SIZE];
  246.     char *leader, *flagstatus;
  247.     //
  248.     ClientName(bs->client, netname, sizeof(netname));
  249.     if (Q_stricmp(netname, bs->teamleader) == 0) leader = "L";
  250.     else leader = " ";
  251.     if (BotCTFCarryingFlag(bs)) {
  252.         if (BotCTFTeam(bs) == TEAM_RED) flagstatus = S_COLOR_RED"F";
  253.         else flagstatus = S_COLOR_BLUE"F";
  254.     }
  255.     else {
  256.         flagstatus = " ";
  257.     }
  258.     switch(bs->ltgtype) {
  259.         case LTG_TEAMHELP:
  260.         {
  261.             EasyClientName(bs->teammate, goalname, sizeof(goalname));
  262.             BotAI_Print(PRT_MESSAGE, "%-20s%s%s: helping %s\n", netname, leader, flagstatus, goalname);
  263.             break;
  264.         }
  265.         case LTG_TEAMACCOMPANY:
  266.         {
  267.             EasyClientName(bs->teammate, goalname, sizeof(goalname));
  268.             BotAI_Print(PRT_MESSAGE, "%-20s%s%s: accompanying %s\n", netname, leader, flagstatus, goalname);
  269.             break;
  270.         }
  271.         case LTG_DEFENDKEYAREA:
  272.         {
  273.             trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname));
  274.             BotAI_Print(PRT_MESSAGE, "%-20s%s%s: defending %s\n", netname, leader, flagstatus, goalname);
  275.             break;
  276.         }
  277.         case LTG_GETITEM:
  278.         {
  279.             trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname));
  280.             BotAI_Print(PRT_MESSAGE, "%-20s%s%s: getting item %s\n", netname, leader, flagstatus, goalname);
  281.             break;
  282.         }
  283.         case LTG_KILL:
  284.         {
  285.             ClientName(bs->teamgoal.entitynum, goalname, sizeof(goalname));
  286.             BotAI_Print(PRT_MESSAGE, "%-20s%s%s: killing %s\n", netname, leader, flagstatus, goalname);
  287.             break;
  288.         }
  289.         case LTG_CAMP:
  290.         case LTG_CAMPORDER:
  291.         {
  292.             BotAI_Print(PRT_MESSAGE, "%-20s%s%s: camping\n", netname, leader, flagstatus);
  293.             break;
  294.         }
  295.         case LTG_PATROL:
  296.         {
  297.             BotAI_Print(PRT_MESSAGE, "%-20s%s%s: patrolling\n", netname, leader, flagstatus);
  298.             break;
  299.         }
  300.         case LTG_GETFLAG:
  301.         {
  302.             BotAI_Print(PRT_MESSAGE, "%-20s%s%s: capturing flag\n", netname, leader, flagstatus);
  303.             break;
  304.         }
  305.         case LTG_RUSHBASE:
  306.         {
  307.             BotAI_Print(PRT_MESSAGE, "%-20s%s%s: rushing base\n", netname, leader, flagstatus);
  308.             break;
  309.         }
  310.         case LTG_RETURNFLAG:
  311.         {
  312.             BotAI_Print(PRT_MESSAGE, "%-20s%s%s: returning flag\n", netname, leader, flagstatus);
  313.             break;
  314.         }
  315.         default:
  316.         {
  317.             BotAI_Print(PRT_MESSAGE, "%-20s%s%s: roaming\n", netname, leader, flagstatus);
  318.             break;
  319.         }
  320.     }
  321. }
  322.  
  323. /*
  324. ==================
  325. BotTeamplayReport
  326. ==================
  327. */
  328. void BotTeamplayReport(void) {
  329.     int i;
  330.     char buf[MAX_INFO_STRING];
  331.  
  332.     BotAI_Print(PRT_MESSAGE, S_COLOR_RED"RED\n");
  333.     for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
  334.         //
  335.         if ( !botstates[i] || !botstates[i]->inuse ) continue;
  336.         //
  337.         trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
  338.         //if no config string or no name
  339.         if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
  340.         //skip spectators
  341.         if (atoi(Info_ValueForKey(buf, "t")) == TEAM_RED) {
  342.             BotReportStatus(botstates[i]);
  343.         }
  344.     }
  345.     BotAI_Print(PRT_MESSAGE, S_COLOR_BLUE"BLUE\n");
  346.     for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
  347.         //
  348.         if ( !botstates[i] || !botstates[i]->inuse ) continue;
  349.         //
  350.         trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
  351.         //if no config string or no name
  352.         if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
  353.         //skip spectators
  354.         if (atoi(Info_ValueForKey(buf, "t")) == TEAM_BLUE) {
  355.             BotReportStatus(botstates[i]);
  356.         }
  357.     }
  358. }
  359.  
  360. /*
  361. ==============
  362. BotInterbreedBots
  363. ==============
  364. */
  365. void BotInterbreedBots(void) {
  366.     float ranks[MAX_CLIENTS];
  367.     int parent1, parent2, child;
  368.     int i;
  369.  
  370.     // get rankings for all the bots
  371.     for (i = 0; i < MAX_CLIENTS; i++) {
  372.         if ( botstates[i] && botstates[i]->inuse ) {
  373.             ranks[i] = botstates[i]->num_kills * 2 - botstates[i]->num_deaths;
  374.         }
  375.         else {
  376.             ranks[i] = -1;
  377.         }
  378.     }
  379.  
  380.     if (trap_GeneticParentsAndChildSelection(MAX_CLIENTS, ranks, &parent1, &parent2, &child)) {
  381.         trap_BotInterbreedGoalFuzzyLogic(botstates[parent1]->gs, botstates[parent2]->gs, botstates[child]->gs);
  382.         trap_BotMutateGoalFuzzyLogic(botstates[child]->gs, 1);
  383.     }
  384.     // reset the kills and deaths
  385.     for (i = 0; i < MAX_CLIENTS; i++) {
  386.         if (botstates[i] && botstates[i]->inuse) {
  387.             botstates[i]->num_kills = 0;
  388.             botstates[i]->num_deaths = 0;
  389.         }
  390.     }
  391. }
  392.  
  393. /*
  394. ==============
  395. BotWriteInterbreeded
  396. ==============
  397. */
  398. void BotWriteInterbreeded(char *filename) {
  399.     float rank, bestrank;
  400.     int i, bestbot;
  401.  
  402.     bestrank = 0;
  403.     bestbot = -1;
  404.     // get the best bot
  405.     for (i = 0; i < MAX_CLIENTS; i++) {
  406.         if ( botstates[i] && botstates[i]->inuse ) {
  407.             rank = botstates[i]->num_kills * 2 - botstates[i]->num_deaths;
  408.         }
  409.         else {
  410.             rank = -1;
  411.         }
  412.         if (rank > bestrank) {
  413.             bestrank = rank;
  414.             bestbot = i;
  415.         }
  416.     }
  417.     if (bestbot >= 0) {
  418.         //write out the new goal fuzzy logic
  419.         trap_BotSaveGoalFuzzyLogic(botstates[bestbot]->gs, filename);
  420.     }
  421. }
  422.  
  423. /*
  424. ==============
  425. BotInterbreedEndMatch
  426.  
  427. add link back into ExitLevel?
  428. ==============
  429. */
  430. void BotInterbreedEndMatch(void) {
  431.  
  432.     if (!bot_interbreed) return;
  433.     bot_interbreedmatchcount++;
  434.     if (bot_interbreedmatchcount >= bot_interbreedcycle.integer) {
  435.         bot_interbreedmatchcount = 0;
  436.         //
  437.         trap_Cvar_Update(&bot_interbreedwrite);
  438.         if (strlen(bot_interbreedwrite.string)) {
  439.             BotWriteInterbreeded(bot_interbreedwrite.string);
  440.             trap_Cvar_Set("bot_interbreedwrite", "");
  441.         }
  442.         BotInterbreedBots();
  443.     }
  444. }
  445.  
  446. /*
  447. ==============
  448. BotInterbreeding
  449. ==============
  450. */
  451. void BotInterbreeding(void) {
  452.     int i;
  453.  
  454.     trap_Cvar_Update(&bot_interbreedchar);
  455.     if (!strlen(bot_interbreedchar.string)) return;
  456.     //make sure we are in tournament mode
  457.     if (gametype != GT_TOURNAMENT) {
  458.         trap_Cvar_Set("g_gametype", va("%d", GT_TOURNAMENT));
  459.         ExitLevel();
  460.         return;
  461.     }
  462.     //shutdown all the bots
  463.     for (i = 0; i < MAX_CLIENTS; i++) {
  464.         if (botstates[i] && botstates[i]->inuse) {
  465.             BotAIShutdownClient(botstates[i]->client);
  466.         }
  467.     }
  468.     //make sure all item weight configs are reloaded and Not shared
  469.     trap_BotLibVarSet("bot_reloadcharacters", "1");
  470.     //add a number of bots using the desired bot character
  471.     for (i = 0; i < bot_interbreedbots.integer; i++) {
  472.         trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s 4 free %i %s%d\n",
  473.                         bot_interbreedchar.string, i * 50, bot_interbreedchar.string, i) );
  474.     }
  475.     //
  476.     trap_Cvar_Set("bot_interbreedchar", "");
  477.     bot_interbreed = qtrue;
  478. }
  479.  
  480. /*
  481. ==============
  482. BotEntityInfo
  483. ==============
  484. */
  485. void BotEntityInfo(int entnum, aas_entityinfo_t *info) {
  486.     trap_AAS_EntityInfo(entnum, info);
  487. }
  488.  
  489. /*
  490. ==============
  491. NumBots
  492. ==============
  493. */
  494. int NumBots(void) {
  495.     return numbots;
  496. }
  497.  
  498. /*
  499. ==============
  500. BotTeamLeader
  501. ==============
  502. */
  503. int BotTeamLeader(bot_state_t *bs) {
  504.     int leader;
  505.  
  506.     leader = ClientFromName(bs->teamleader);
  507.     if (leader < 0) return qfalse;
  508.     if (!botstates[leader] || !botstates[leader]->inuse) return qfalse;
  509.     return qtrue;
  510. }
  511.  
  512. /*
  513. ==============
  514. AngleDifference
  515. ==============
  516. */
  517. float AngleDifference(float ang1, float ang2) {
  518.     float diff;
  519.  
  520.     diff = ang1 - ang2;
  521.     if (ang1 > ang2) {
  522.         if (diff > 180.0) diff -= 360.0;
  523.     }
  524.     else {
  525.         if (diff < -180.0) diff += 360.0;
  526.     }
  527.     return diff;
  528. }
  529.  
  530. /*
  531. ==============
  532. BotChangeViewAngle
  533. ==============
  534. */
  535. float BotChangeViewAngle(float angle, float ideal_angle, float speed) {
  536.     float move;
  537.  
  538.     angle = AngleMod(angle);
  539.     ideal_angle = AngleMod(ideal_angle);
  540.     if (angle == ideal_angle) return angle;
  541.     move = ideal_angle - angle;
  542.     if (ideal_angle > angle) {
  543.         if (move > 180.0) move -= 360.0;
  544.     }
  545.     else {
  546.         if (move < -180.0) move += 360.0;
  547.     }
  548.     if (move > 0) {
  549.         if (move > speed) move = speed;
  550.     }
  551.     else {
  552.         if (move < -speed) move = -speed;
  553.     }
  554.     return AngleMod(angle + move);
  555. }
  556.  
  557. /*
  558. ==============
  559. BotChangeViewAngles
  560. ==============
  561. */
  562. void BotChangeViewAngles(bot_state_t *bs, float thinktime) {
  563.     float diff, factor, maxchange, anglespeed, disired_speed;
  564.     int i;
  565.  
  566.     if (bs->ideal_viewangles[PITCH] > 180) bs->ideal_viewangles[PITCH] -= 360;
  567.     //
  568.     if (bs->enemy >= 0) {
  569.         factor = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_FACTOR, 0.01, 1);
  570.         maxchange = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_MAXCHANGE, 1, 1800);
  571.     }
  572.     else {
  573.         factor = 0.05;
  574.         maxchange = 360;
  575.     }
  576.     if (maxchange < 240) maxchange = 240;
  577.     maxchange *= thinktime;
  578.     for (i = 0; i < 2; i++) {
  579.         //
  580.         if (bot_challenge.integer) {
  581.             //smooth slowdown view model
  582.             diff = abs(AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]));
  583.             anglespeed = diff * factor;
  584.             if (anglespeed > maxchange) anglespeed = maxchange;
  585.             bs->viewangles[i] = BotChangeViewAngle(bs->viewangles[i],
  586.                                             bs->ideal_viewangles[i], anglespeed);
  587.         }
  588.         else {
  589.             //over reaction view model
  590.             bs->viewangles[i] = AngleMod(bs->viewangles[i]);
  591.             bs->ideal_viewangles[i] = AngleMod(bs->ideal_viewangles[i]);
  592.             diff = AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]);
  593.             disired_speed = diff * factor;
  594.             bs->viewanglespeed[i] += (bs->viewanglespeed[i] - disired_speed);
  595.             if (bs->viewanglespeed[i] > 180) bs->viewanglespeed[i] = maxchange;
  596.             if (bs->viewanglespeed[i] < -180) bs->viewanglespeed[i] = -maxchange;
  597.             anglespeed = bs->viewanglespeed[i];
  598.             if (anglespeed > maxchange) anglespeed = maxchange;
  599.             if (anglespeed < -maxchange) anglespeed = -maxchange;
  600.             bs->viewangles[i] += anglespeed;
  601.             bs->viewangles[i] = AngleMod(bs->viewangles[i]);
  602.             //demping
  603.             bs->viewanglespeed[i] *= 0.45 * (1 - factor);
  604.         }
  605.         //BotAI_Print(PRT_MESSAGE, "ideal_angles %f %f\n", bs->ideal_viewangles[0], bs->ideal_viewangles[1], bs->ideal_viewangles[2]);`
  606.         //bs->viewangles[i] = bs->ideal_viewangles[i];
  607.     }
  608.     //bs->viewangles[PITCH] = 0;
  609.     if (bs->viewangles[PITCH] > 180) bs->viewangles[PITCH] -= 360;
  610.     //elementary action: view
  611.     trap_EA_View(bs->client, bs->viewangles);
  612. }
  613.  
  614. /*
  615. ==============
  616. BotInputToUserCommand
  617. ==============
  618. */
  619. void BotInputToUserCommand(bot_input_t *bi, usercmd_t *ucmd, int delta_angles[3], int time) {
  620.     vec3_t angles, forward, right;
  621.     short temp;
  622.     int j;
  623.  
  624.     //clear the whole structure
  625.     memset(ucmd, 0, sizeof(usercmd_t));
  626.     //
  627.     //Com_Printf("dir = %f %f %f speed = %f\n", bi->dir[0], bi->dir[1], bi->dir[2], bi->speed);
  628.     //the duration for the user command in milli seconds
  629.     ucmd->serverTime = time;
  630.     //
  631.     if (bi->actionflags & ACTION_DELAYEDJUMP) {
  632.         bi->actionflags |= ACTION_JUMP;
  633.         bi->actionflags &= ~ACTION_DELAYEDJUMP;
  634.     }
  635.     //set the buttons
  636.     if (bi->actionflags & ACTION_RESPAWN) ucmd->buttons = BUTTON_ATTACK;
  637.     if (bi->actionflags & ACTION_ATTACK) ucmd->buttons |= BUTTON_ATTACK;
  638.     if (bi->actionflags & ACTION_TALK) ucmd->buttons |= BUTTON_TALK;
  639.     if (bi->actionflags & ACTION_GESTURE) ucmd->buttons |= BUTTON_GESTURE;
  640.     if (bi->actionflags & ACTION_USE) ucmd->buttons |= BUTTON_USE_HOLDABLE;
  641.     if (bi->actionflags & ACTION_WALK) ucmd->buttons |= BUTTON_WALKING;
  642.     ucmd->weapon = bi->weapon;
  643.     //set the view angles
  644.     //NOTE: the ucmd->angles are the angles WITHOUT the delta angles
  645.     ucmd->angles[PITCH] = ANGLE2SHORT(bi->viewangles[PITCH]);
  646.     ucmd->angles[YAW] = ANGLE2SHORT(bi->viewangles[YAW]);
  647.     ucmd->angles[ROLL] = ANGLE2SHORT(bi->viewangles[ROLL]);
  648.     //subtract the delta angles
  649.     for (j = 0; j < 3; j++) {
  650.         temp = ucmd->angles[j] - delta_angles[j];
  651.         /*NOTE: disabled because temp should be mod first
  652.         if ( j == PITCH ) {
  653.             // don't let the player look up or down more than 90 degrees
  654.             if ( temp > 16000 ) temp = 16000;
  655.             else if ( temp < -16000 ) temp = -16000;
  656.         }
  657.         */
  658.         ucmd->angles[j] = temp;
  659.     }
  660.     //NOTE: movement is relative to the REAL view angles
  661.     //get the horizontal forward and right vector
  662.     //get the pitch in the range [-180, 180]
  663.     if (bi->dir[2]) angles[PITCH] = bi->viewangles[PITCH];
  664.     else angles[PITCH] = 0;
  665.     angles[YAW] = bi->viewangles[YAW];
  666.     angles[ROLL] = 0;
  667.     AngleVectors(angles, forward, right, NULL);
  668.     //bot input speed is in the range [0, 400]
  669.     bi->speed = bi->speed * 127 / 400;
  670.     //set the view independent movement
  671.     ucmd->forwardmove = DotProduct(forward, bi->dir) * bi->speed;
  672.     ucmd->rightmove = DotProduct(right, bi->dir) * bi->speed;
  673.     ucmd->upmove = abs(forward[2]) * bi->dir[2] * bi->speed;
  674.     //normal keyboard movement
  675.     if (bi->actionflags & ACTION_MOVEFORWARD) ucmd->forwardmove += 127;
  676.     if (bi->actionflags & ACTION_MOVEBACK) ucmd->forwardmove -= 127;
  677.     if (bi->actionflags & ACTION_MOVELEFT) ucmd->rightmove -= 127;
  678.     if (bi->actionflags & ACTION_MOVERIGHT) ucmd->rightmove += 127;
  679.     //jump/moveup
  680.     if (bi->actionflags & ACTION_JUMP) ucmd->upmove += 127;
  681.     //crouch/movedown
  682.     if (bi->actionflags & ACTION_CROUCH) ucmd->upmove -= 127;
  683.     //
  684.     //Com_Printf("forward = %d right = %d up = %d\n", ucmd.forwardmove, ucmd.rightmove, ucmd.upmove);
  685.     //Com_Printf("ucmd->serverTime = %d\n", ucmd->serverTime);
  686. }
  687.  
  688. /*
  689. ==============
  690. BotUpdateInput
  691. ==============
  692. */
  693. void BotUpdateInput(bot_state_t *bs, int time, int elapsed_time) {
  694.     bot_input_t bi;
  695.     int j;
  696.  
  697.     //add the delta angles to the bot's current view angles
  698.     for (j = 0; j < 3; j++) {
  699.         bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
  700.     }
  701.     //change the bot view angles
  702.     BotChangeViewAngles(bs, (float) elapsed_time / 1000);
  703.     //retrieve the bot input
  704.     trap_EA_GetInput(bs->client, (float) time / 1000, &bi);
  705.     //respawn hack
  706.     if (bi.actionflags & ACTION_RESPAWN) {
  707.         if (bs->lastucmd.buttons & BUTTON_ATTACK) bi.actionflags &= ~(ACTION_RESPAWN|ACTION_ATTACK);
  708.     }
  709.     //convert the bot input to a usercmd
  710.     BotInputToUserCommand(&bi, &bs->lastucmd, bs->cur_ps.delta_angles, time);
  711.     //subtract the delta angles
  712.     for (j = 0; j < 3; j++) {
  713.         bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
  714.     }
  715. }
  716.  
  717. /*
  718. ==============
  719. BotAIRegularUpdate
  720. ==============
  721. */
  722. void BotAIRegularUpdate(void) {
  723.     if (regularupdate_time < trap_AAS_Time()) {
  724.         trap_BotUpdateEntityItems();
  725.         regularupdate_time = trap_AAS_Time() + 0.3;
  726.     }
  727. }
  728.  
  729. /*
  730. ==============
  731. BotAI
  732. ==============
  733. */
  734. int BotAI(int client, float thinktime) {
  735.     bot_state_t *bs;
  736.     char buf[1024], *args;
  737.     int j;
  738.  
  739.     trap_EA_ResetInput(client);
  740.     //
  741.     bs = botstates[client];
  742.     if (!bs || !bs->inuse) {
  743.         BotAI_Print(PRT_FATAL, "BotAI: client %d is not setup\n", client);
  744.         return qfalse;
  745.     }
  746.  
  747.     //retrieve the current client state
  748.     BotAI_GetClientState( client, &bs->cur_ps );
  749.  
  750.     //retrieve any waiting console messages
  751.     while( trap_BotGetConsoleMessage(client, buf, sizeof(buf)) ) {
  752.         //have buf point to the command and args to the command arguments
  753.         args = strchr( buf, ' ');
  754.         if (!args) continue;
  755.         *args++ = '\0';
  756.  
  757.         //remove color espace sequences from the arguments
  758.         Q_CleanStr( args );
  759.  
  760.         if (!Q_stricmp(buf, "cp "))
  761.             { /*CenterPrintf*/ }
  762.         else if (!Q_stricmp(buf, "cs"))
  763.             { /*ConfigStringModified*/ }
  764.         else if (!Q_stricmp(buf, "print")) {
  765.             //remove first and last quote from the chat message
  766.             memmove(args, args+1, strlen(args));
  767.             args[strlen(args)-1] = '\0';
  768.             trap_BotQueueConsoleMessage(bs->cs, CMS_NORMAL, args);
  769.         }
  770.         else if (!Q_stricmp(buf, "chat")) {
  771.             //remove first and last quote from the chat message
  772.             memmove(args, args+1, strlen(args));
  773.             args[strlen(args)-1] = '\0';
  774.             trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args);
  775.         }
  776.         else if (!Q_stricmp(buf, "tchat")) {
  777.             //remove first and last quote from the chat message
  778.             memmove(args, args+1, strlen(args));
  779.             args[strlen(args)-1] = '\0';
  780.             trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args);
  781.         }
  782.         else if (!Q_stricmp(buf, "scores"))
  783.             { /*FIXME: parse scores?*/ }
  784.         else if (!Q_stricmp(buf, "clientLevelShot"))
  785.             { /*ignore*/ }
  786.     }
  787.     //add the delta angles to the bot's current view angles
  788.     for (j = 0; j < 3; j++) {
  789.         bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
  790.     }
  791.     //increase the local time of the bot
  792.     bs->ltime += thinktime;
  793.     //
  794.     bs->thinktime = thinktime;
  795.     //origin of the bot
  796.     VectorCopy(bs->cur_ps.origin, bs->origin);
  797.     //eye coordinates of the bot
  798.     VectorCopy(bs->cur_ps.origin, bs->eye);
  799.     bs->eye[2] += bs->cur_ps.viewheight;
  800.     //get the area the bot is in
  801.     bs->areanum = BotPointAreaNum(bs->origin);
  802.     //the real AI
  803.     BotDeathmatchAI(bs, thinktime);
  804.     //set the weapon selection every AI frame
  805.     trap_EA_SelectWeapon(bs->client, bs->weaponnum);
  806.     //subtract the delta angles
  807.     for (j = 0; j < 3; j++) {
  808.         bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
  809.     }
  810.     //everything was ok
  811.     return qtrue;
  812. }
  813.  
  814. /*
  815. ==================
  816. BotScheduleBotThink
  817. ==================
  818. */
  819. void BotScheduleBotThink(void) {
  820.     int i, botnum;
  821.  
  822.     botnum = 0;
  823.  
  824.     for( i = 0; i < MAX_CLIENTS; i++ ) {
  825.         if( !botstates[i] || !botstates[i]->inuse ) {
  826.             continue;
  827.         }
  828.         //initialize the bot think residual time
  829.         botstates[i]->botthink_residual = bot_thinktime.integer * botnum / numbots;
  830.         botnum++;
  831.     }
  832. }
  833.  
  834. /*
  835. ==============
  836. BotAISetupClient
  837. ==============
  838. */
  839. int BotAISetupClient(int client, struct bot_settings_s *settings) {
  840.     char filename[MAX_PATH], name[MAX_PATH], gender[MAX_PATH];
  841.     bot_state_t *bs;
  842.     int errnum;
  843.  
  844.     if (!botstates[client]) botstates[client] = G_Alloc(sizeof(bot_state_t));
  845.     bs = botstates[client];
  846.  
  847.     if (bs && bs->inuse) {
  848.         BotAI_Print(PRT_FATAL, "BotAISetupClient: client %d already setup\n", client);
  849.         return qfalse;
  850.     }
  851.  
  852.     if (!trap_AAS_Initialized()) {
  853.         BotAI_Print(PRT_FATAL, "AAS not initialized\n");
  854.         return qfalse;
  855.     }
  856.  
  857.     //load the bot character
  858.     bs->character = trap_BotLoadCharacter(settings->characterfile, settings->skill);
  859.     if (!bs->character) {
  860.         BotAI_Print(PRT_FATAL, "couldn't load skill %d from %s\n", settings->skill, settings->characterfile);
  861.         return qfalse;
  862.     }
  863.     //copy the settings
  864.     memcpy(&bs->settings, settings, sizeof(bot_settings_t));
  865.     //allocate a goal state
  866.     bs->gs = trap_BotAllocGoalState(client);
  867.     //load the item weights
  868.     trap_Characteristic_String(bs->character, CHARACTERISTIC_ITEMWEIGHTS, filename, MAX_PATH);
  869.     errnum = trap_BotLoadItemWeights(bs->gs, filename);
  870.     if (errnum != BLERR_NOERROR) {
  871.         trap_BotFreeGoalState(bs->gs);
  872.         return qfalse;
  873.     }
  874.     //allocate a weapon state
  875.     bs->ws = trap_BotAllocWeaponState();
  876.     //load the weapon weights
  877.     trap_Characteristic_String(bs->character, CHARACTERISTIC_WEAPONWEIGHTS, filename, MAX_PATH);
  878.     errnum = trap_BotLoadWeaponWeights(bs->ws, filename);
  879.     if (errnum != BLERR_NOERROR) {
  880.         trap_BotFreeGoalState(bs->gs);
  881.         trap_BotFreeWeaponState(bs->ws);
  882.         return qfalse;
  883.     }
  884.     //allocate a chat state
  885.     bs->cs = trap_BotAllocChatState();
  886.     //load the chat file
  887.     trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_FILE, filename, MAX_PATH);
  888.     trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_NAME, name, MAX_PATH);
  889.     errnum = trap_BotLoadChatFile(bs->cs, filename, name);
  890.     if (errnum != BLERR_NOERROR) {
  891.         trap_BotFreeChatState(bs->cs);
  892.         trap_BotFreeGoalState(bs->gs);
  893.         trap_BotFreeWeaponState(bs->ws);
  894.         return qfalse;
  895.     }
  896.     //get the gender characteristic
  897.     trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, MAX_PATH);
  898.     //set the chat gender
  899.     if (*gender == 'f' || *gender == 'F') trap_BotSetChatGender(bs->cs, CHAT_GENDERFEMALE);
  900.     else if (*gender == 'm' || *gender == 'M') trap_BotSetChatGender(bs->cs, CHAT_GENDERMALE);
  901.     else trap_BotSetChatGender(bs->cs, CHAT_GENDERLESS);
  902.  
  903.     bs->inuse = qtrue;
  904.     bs->client = client;
  905.     bs->entitynum = client;
  906.     bs->setupcount = 4;
  907.     bs->entergame_time = trap_AAS_Time();
  908.     bs->ms = trap_BotAllocMoveState();
  909.     bs->walker = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_WALKER, 0, 1);
  910.     numbots++;
  911.  
  912.     if (trap_Cvar_VariableIntegerValue("bot_testichat")) {
  913.         trap_BotLibVarSet("bot_testichat", "1");
  914.         BotChatTest(bs);
  915.     }
  916.     //NOTE: reschedule the bot thinking
  917.     BotScheduleBotThink();
  918.     //if interbreeding start with a mutation
  919.     if (bot_interbreed) {
  920.         trap_BotMutateGoalFuzzyLogic(bs->gs, 1);
  921.     }
  922.     //bot has been setup succesfully
  923.     return qtrue;
  924. }
  925.  
  926. /*
  927. ==============
  928. BotAIShutdownClient
  929. ==============
  930. */
  931. int BotAIShutdownClient(int client) {
  932.     bot_state_t *bs;
  933.  
  934.     bs = botstates[client];
  935.     if (!bs || !bs->inuse) {
  936.         //BotAI_Print(PRT_ERROR, "BotAIShutdownClient: client %d already shutdown\n", client);
  937.         return qfalse;
  938.     }
  939.  
  940.     if (BotChat_ExitGame(bs)) {
  941.         trap_BotEnterChat(bs->cs, bs->client, CHAT_ALL);
  942.     }
  943.  
  944.     trap_BotFreeMoveState(bs->ms);
  945.     //free the goal state`            
  946.     trap_BotFreeGoalState(bs->gs);
  947.     //free the chat file
  948.     trap_BotFreeChatState(bs->cs);
  949.     //free the weapon weights
  950.     trap_BotFreeWeaponState(bs->ws);
  951.     //free the bot character
  952.     trap_BotFreeCharacter(bs->character);
  953.     //
  954.     BotFreeWaypoints(bs->checkpoints);
  955.     BotFreeWaypoints(bs->patrolpoints);
  956.     //clear the bot state
  957.     memset(bs, 0, sizeof(bot_state_t));
  958.     //set the inuse flag to qfalse
  959.     bs->inuse = qfalse;
  960.     //there's one bot less
  961.     numbots--;
  962.     //everything went ok
  963.     return qtrue;
  964. }
  965.  
  966. /*
  967. ==============
  968. BotResetState
  969.  
  970. called when a bot enters the intermission or observer mode and
  971. when the level is changed
  972. ==============
  973. */
  974. void BotResetState(bot_state_t *bs) {
  975.     int client, entitynum, inuse;
  976.     int movestate, goalstate, chatstate, weaponstate;
  977.     bot_settings_t settings;
  978.     int character;
  979.     playerState_t ps;                            //current player state
  980.     float entergame_time;
  981.  
  982.     //save some things that should not be reset here
  983.     memcpy(&settings, &bs->settings, sizeof(bot_settings_t));
  984.     memcpy(&ps, &bs->cur_ps, sizeof(playerState_t));
  985.     inuse = bs->inuse;
  986.     client = bs->client;
  987.     entitynum = bs->entitynum;
  988.     character = bs->character;
  989.     movestate = bs->ms;
  990.     goalstate = bs->gs;
  991.     chatstate = bs->cs;
  992.     weaponstate = bs->ws;
  993.     entergame_time = bs->entergame_time;
  994.     //free checkpoints and patrol points
  995.     BotFreeWaypoints(bs->checkpoints);
  996.     BotFreeWaypoints(bs->patrolpoints);
  997.     //reset the whole state
  998.     memset(bs, 0, sizeof(bot_state_t));
  999.     //copy back some state stuff that should not be reset
  1000.     bs->ms = movestate;
  1001.     bs->gs = goalstate;
  1002.     bs->cs = chatstate;
  1003.     bs->ws = weaponstate;
  1004.     memcpy(&bs->cur_ps, &ps, sizeof(playerState_t));
  1005.     memcpy(&bs->settings, &settings, sizeof(bot_settings_t));
  1006.     bs->inuse = inuse;
  1007.     bs->client = client;
  1008.     bs->entitynum = entitynum;
  1009.     bs->character = character;
  1010.     bs->entergame_time = entergame_time;
  1011.     //reset several states
  1012.     if (bs->ms) trap_BotResetMoveState(bs->ms);
  1013.     if (bs->gs) trap_BotResetGoalState(bs->gs);
  1014.     if (bs->ws) trap_BotResetWeaponState(bs->ws);
  1015.     if (bs->gs) trap_BotResetAvoidGoals(bs->gs);
  1016.     if (bs->ms) trap_BotResetAvoidReach(bs->ms);
  1017. }
  1018.  
  1019. /*
  1020. ==============
  1021. BotAILoadMap
  1022. ==============
  1023. */
  1024. int BotAILoadMap( int restart ) {
  1025.     int            i;
  1026.     vmCvar_t    mapname;
  1027.  
  1028.     if (!restart) {
  1029.         trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM );
  1030.         trap_BotLibLoadMap( mapname.string );
  1031.     }
  1032.  
  1033.     for (i = 0; i < MAX_CLIENTS; i++) {
  1034.         if (botstates[i] && botstates[i]->inuse) {
  1035.             BotResetState( botstates[i] );
  1036.             botstates[i]->setupcount = 4;
  1037.         }
  1038.     }
  1039.  
  1040.     BotSetupDeathmatchAI();
  1041.  
  1042.     return qtrue;
  1043. }
  1044.  
  1045. /*
  1046. ==================
  1047. BotAIStartFrame
  1048. ==================
  1049. */
  1050. int BotAIStartFrame(int time) {
  1051.     int i;
  1052.     gentity_t    *ent;
  1053.     bot_entitystate_t state;
  1054.     int elapsed_time, thinktime;
  1055.     static int local_time;
  1056.     static int botlib_residual;
  1057.     static int lastbotthink_time;
  1058.  
  1059.     G_CheckBotSpawn();
  1060.  
  1061.     trap_Cvar_Update(&bot_rocketjump);
  1062.     trap_Cvar_Update(&bot_grapple);
  1063.     trap_Cvar_Update(&bot_fastchat);
  1064.     trap_Cvar_Update(&bot_nochat);
  1065.     trap_Cvar_Update(&bot_testrchat);
  1066.     trap_Cvar_Update(&bot_thinktime);
  1067.     trap_Cvar_Update(&bot_memorydump);
  1068.     trap_Cvar_Update(&bot_pause);
  1069.     trap_Cvar_Update(&bot_report);
  1070.  
  1071.     if (bot_report.integer) {
  1072.         BotTeamplayReport();
  1073.         trap_Cvar_Set("bot_report", "0");
  1074.     }
  1075.  
  1076.     if (bot_pause.integer) {
  1077.         // execute bot user commands every frame
  1078.         for( i = 0; i < MAX_CLIENTS; i++ ) {
  1079.             if( !botstates[i] || !botstates[i]->inuse ) {
  1080.                 continue;
  1081.             }
  1082.             if( g_entities[i].client->pers.connected != CON_CONNECTED ) {
  1083.                 continue;
  1084.             }
  1085.             botstates[i]->lastucmd.forwardmove = 0;
  1086.             botstates[i]->lastucmd.rightmove = 0;
  1087.             botstates[i]->lastucmd.upmove = 0;
  1088.             botstates[i]->lastucmd.buttons = 0;
  1089.             botstates[i]->lastucmd.serverTime = time;
  1090.             trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd);
  1091.         }
  1092.         return qtrue;
  1093.     }
  1094.  
  1095.     if (bot_memorydump.integer) {
  1096.         trap_BotLibVarSet("memorydump", "1");
  1097.         trap_Cvar_Set("bot_memorydump", "0");
  1098.     }
  1099.     //check if bot interbreeding is activated
  1100.     BotInterbreeding();
  1101.     //cap the bot think time
  1102.     if (bot_thinktime.integer > 200) {
  1103.         trap_Cvar_Set("bot_thinktime", "200");
  1104.     }
  1105.     //if the bot think time changed we should reschedule the bots
  1106.     if (bot_thinktime.integer != lastbotthink_time) {
  1107.         lastbotthink_time = bot_thinktime.integer;
  1108.         BotScheduleBotThink();
  1109.     }
  1110.  
  1111.     elapsed_time = time - local_time;
  1112.     local_time = time;
  1113.  
  1114.     botlib_residual += elapsed_time;
  1115.  
  1116.     if (elapsed_time > bot_thinktime.integer) thinktime = elapsed_time;
  1117.     else thinktime = bot_thinktime.integer;
  1118.  
  1119.     // update the bot library
  1120.     if ( botlib_residual >= thinktime ) {
  1121.         botlib_residual -= thinktime;
  1122.  
  1123.         trap_BotLibStartFrame((float) time / 1000);
  1124.  
  1125.         if (!trap_AAS_Initialized()) return qfalse;
  1126.  
  1127.         //update entities in the botlib
  1128.         for (i = 0; i < MAX_GENTITIES; i++) {
  1129.             ent = &g_entities[i];
  1130.             if (!ent->inuse) continue;
  1131.             if (!ent->r.linked) continue;
  1132.             if (ent->r.svFlags & SVF_NOCLIENT) continue;
  1133.             //
  1134.             memset(&state, 0, sizeof(bot_entitystate_t));
  1135.             //
  1136.             VectorCopy(ent->r.currentOrigin, state.origin);
  1137.             VectorCopy(ent->r.currentAngles, state.angles);
  1138.             VectorCopy(ent->s.origin2, state.old_origin);
  1139.             VectorCopy(ent->r.mins, state.mins);
  1140.             VectorCopy(ent->r.maxs, state.maxs);
  1141.             state.type = ent->s.eType;
  1142.             state.flags = ent->s.eFlags;
  1143.             if (ent->r.bmodel) state.solid = SOLID_BSP;
  1144.             else state.solid = SOLID_BBOX;
  1145.             state.groundent = ent->s.groundEntityNum;
  1146.             state.modelindex = ent->s.modelindex;
  1147.             state.modelindex2 = ent->s.modelindex2;
  1148.             state.frame = ent->s.frame;
  1149.             state.event = ent->s.event;
  1150.             state.eventParm = ent->s.eventParm;
  1151.             state.powerups = ent->s.powerups;
  1152.             state.legsAnim = ent->s.legsAnim;
  1153.             state.torsoAnim = ent->s.torsoAnim;
  1154.             state.weapon = ent->s.weapon;
  1155.             //
  1156.             trap_BotLibUpdateEntity(i, &state);
  1157.         }
  1158.  
  1159.         BotAIRegularUpdate();
  1160.  
  1161.     }
  1162.  
  1163.     // execute scheduled bot AI
  1164.     for( i = 0; i < MAX_CLIENTS; i++ ) {
  1165.         if( !botstates[i] || !botstates[i]->inuse ) {
  1166.             continue;
  1167.         }
  1168.         //
  1169.         botstates[i]->botthink_residual += elapsed_time;
  1170.         //
  1171.         if ( botstates[i]->botthink_residual >= thinktime ) {
  1172.             botstates[i]->botthink_residual -= thinktime;
  1173.  
  1174.             if (!trap_AAS_Initialized()) return qfalse;
  1175.  
  1176.             if (g_entities[i].client->pers.connected == CON_CONNECTED) {
  1177.                 BotAI(i, (float) thinktime / 1000);
  1178.             }
  1179.         }
  1180.     }
  1181.  
  1182.  
  1183.     // execute bot user commands every frame
  1184.     for( i = 0; i < MAX_CLIENTS; i++ ) {
  1185.         if( !botstates[i] || !botstates[i]->inuse ) {
  1186.             continue;
  1187.         }
  1188.         if( g_entities[i].client->pers.connected != CON_CONNECTED ) {
  1189.             continue;
  1190.         }
  1191.  
  1192.         BotUpdateInput(botstates[i], time, elapsed_time);
  1193.         trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd);
  1194.     }
  1195.  
  1196.     return qtrue;
  1197. }
  1198.  
  1199. /*
  1200. ==============
  1201. BotInitLibrary
  1202. ==============
  1203. */
  1204. int BotInitLibrary(void) {
  1205.     char buf[144];
  1206.  
  1207.     //set the maxclients and maxentities library variables before calling BotSetupLibrary
  1208.     trap_Cvar_VariableStringBuffer("sv_maxclients", buf, sizeof(buf));
  1209.     if (!strlen(buf)) strcpy(buf, "8");
  1210.     trap_BotLibVarSet("maxclients", buf);
  1211.     Com_sprintf(buf, sizeof(buf), "%d", MAX_GENTITIES);
  1212.     trap_BotLibVarSet("maxentities", buf);
  1213.     //bsp checksum
  1214.     trap_Cvar_VariableStringBuffer("sv_mapChecksum", buf, sizeof(buf));
  1215.     if (strlen(buf)) trap_BotLibVarSet("sv_mapChecksum", buf);
  1216.     //maximum number of aas links
  1217.     trap_Cvar_VariableStringBuffer("max_aaslinks", buf, sizeof(buf));
  1218.     if (strlen(buf)) trap_BotLibVarSet("max_aaslinks", buf);
  1219.     //maximum number of items in a level
  1220.     trap_Cvar_VariableStringBuffer("max_levelitems", buf, sizeof(buf));
  1221.     if (strlen(buf)) trap_BotLibVarSet("max_levelitems", buf);
  1222.     //game type
  1223.     trap_Cvar_VariableStringBuffer("g_gametype", buf, sizeof(buf));
  1224.     if (!strlen(buf)) strcpy(buf, "0");
  1225.     trap_BotLibVarSet("g_gametype", buf);
  1226.     //bot developer mode and log file
  1227.     trap_Cvar_VariableStringBuffer("bot_developer", buf, sizeof(buf));
  1228.     if (!strlen(buf)) strcpy(buf, "0");
  1229.     trap_BotLibVarSet("bot_developer", buf);
  1230.     trap_BotLibVarSet("log", buf);
  1231.     //no chatting
  1232.     trap_Cvar_VariableStringBuffer("bot_nochat", buf, sizeof(buf));
  1233.     if (strlen(buf)) trap_BotLibVarSet("nochat", "0");
  1234.     //visualize jump pads
  1235.     trap_Cvar_VariableStringBuffer("bot_visualizejumppads", buf, sizeof(buf));
  1236.     if (strlen(buf)) trap_BotLibVarSet("bot_visualizejumppads", buf);
  1237.     //forced clustering calculations
  1238.     trap_Cvar_VariableStringBuffer("bot_forceclustering", buf, sizeof(buf));
  1239.     if (strlen(buf)) trap_BotLibVarSet("forceclustering", buf);
  1240.     //forced reachability calculations
  1241.     trap_Cvar_VariableStringBuffer("bot_forcereachability", buf, sizeof(buf));
  1242.     if (strlen(buf)) trap_BotLibVarSet("forcereachability", buf);
  1243.     //force writing of AAS to file
  1244.     trap_Cvar_VariableStringBuffer("bot_forcewrite", buf, sizeof(buf));
  1245.     if (strlen(buf)) trap_BotLibVarSet("forcewrite", buf);
  1246.     //no AAS optimization
  1247.     trap_Cvar_VariableStringBuffer("bot_aasoptimize", buf, sizeof(buf));
  1248.     if (strlen(buf)) trap_BotLibVarSet("aasoptimize", buf);
  1249.     //reload instead of cache bot character files
  1250.     trap_Cvar_VariableStringBuffer("bot_reloadcharacters", buf, sizeof(buf));
  1251.     if (!strlen(buf)) strcpy(buf, "0");
  1252.     trap_BotLibVarSet("bot_reloadcharacters", buf);
  1253.     //base directory
  1254.     trap_Cvar_VariableStringBuffer("fs_basepath", buf, sizeof(buf));
  1255.     if (strlen(buf)) trap_BotLibVarSet("basedir", buf);
  1256.     //game directory
  1257.     trap_Cvar_VariableStringBuffer("fs_game", buf, sizeof(buf));
  1258.     if (strlen(buf)) trap_BotLibVarSet("gamedir", buf);
  1259.     //cd directory
  1260.     trap_Cvar_VariableStringBuffer("fs_cdpath", buf, sizeof(buf));
  1261.     if (strlen(buf)) trap_BotLibVarSet("cddir", buf);
  1262.     //setup the bot library
  1263.     return trap_BotLibSetup();
  1264. }
  1265.  
  1266. /*
  1267. ==============
  1268. BotAISetup
  1269. ==============
  1270. */
  1271. int BotAISetup( int restart ) {
  1272.     int            errnum;
  1273.  
  1274. #ifdef RANDOMIZE
  1275.     srand((unsigned)time(NULL));
  1276. #endif //RANDOMIZE
  1277.  
  1278.  
  1279.     trap_Cvar_Register(&bot_thinktime, "bot_thinktime", "100", CVAR_CHEAT);
  1280.     trap_Cvar_Register(&bot_memorydump, "bot_memorydump", "0", CVAR_CHEAT);
  1281.     trap_Cvar_Register(&bot_pause, "bot_pause", "0", CVAR_CHEAT);
  1282.     trap_Cvar_Register(&bot_report, "bot_report", "0", CVAR_CHEAT);
  1283.     trap_Cvar_Register(&bot_testsolid, "bot_testsolid", "0", CVAR_CHEAT);
  1284.     trap_Cvar_Register(&bot_interbreedchar, "bot_interbreedchar", "", 0);
  1285.     trap_Cvar_Register(&bot_interbreedbots, "bot_interbreedbots", "10", 0);
  1286.     trap_Cvar_Register(&bot_interbreedcycle, "bot_interbreedcycle", "20", 0);
  1287.     trap_Cvar_Register(&bot_interbreedwrite, "bot_interbreedwrite", "", 0);
  1288.  
  1289.     //if the game is restarted for a tournament
  1290.     if (restart) {
  1291.         return qtrue;
  1292.     }
  1293.  
  1294.     //initialize the bot states
  1295.     memset( botstates, 0, sizeof(botstates) );
  1296.  
  1297.     errnum = BotInitLibrary();
  1298.     if (errnum != BLERR_NOERROR) return qfalse;
  1299.     return qtrue;
  1300. }
  1301.  
  1302. /*
  1303. ==============
  1304. BotAIShutdown
  1305. ==============
  1306. */
  1307. int BotAIShutdown( int restart ) {
  1308.  
  1309.     int i;
  1310.  
  1311.     //if the game is restarted for a tournament
  1312.     if ( restart ) {
  1313.         //shutdown all the bots in the botlib
  1314.         for (i = 0; i < MAX_CLIENTS; i++) {
  1315.             if (botstates[i] && botstates[i]->inuse) {
  1316.                 BotAIShutdownClient(botstates[i]->client);
  1317.             }
  1318.         }
  1319.         //don't shutdown the bot library
  1320.     }
  1321.     else {
  1322.         trap_BotLibShutdown();
  1323.     }
  1324.     return qtrue;
  1325. }
  1326.  
  1327.